package com.yc.cloud.common.orm;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 * MyBatis-Plus 代码生成器
 * </p>
 *
 * @author 橘子
 * @since 2024-04-12
 */
public class MybatisPlusGenerator {

    // 基础信息配置
    private static final String db = "yccloud17_aibox";
    private static final String URL = "jdbc:mysql://localhost:3306/" + db + "?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "HR@2025";
    private static final String author = "kinggu";
    private static final String basePackageName = "com.yc.cloud";

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入" + tip + "：");
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "！");
    }

    /**
     * <p>
     * 获取要生成的表
     * </p>
     */
    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
    
    /**
     * 生成自定义文件（DTO和VO）
     */
    private static void generateCustomFiles(String moduleName, List<String> tableNames, String projectPath) {
        try {
            // 初始化Velocity引擎
            VelocityEngine ve = new VelocityEngine();
            ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
            ve.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
            ve.setProperty("output.encoding", "UTF-8");
            ve.init();
            
            // DTO请求参数文件输出路径
            String dtoReqPath = projectPath + "/" + moduleName + "/src/main/java/com/yc/cloud/" + moduleName + "/dto/request/";
            // VO响应参数文件输出路径
            String voPath = projectPath + "/" + moduleName + "/src/main/java/com/yc/cloud/" + moduleName + "/vo/";
            
            // 确保目录存在
            new File(dtoReqPath).mkdirs();
            new File(voPath).mkdirs();
            
            // 获取数据库连接
            Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            
            for (String tableName : tableNames) {
                // 获取表信息
                DatabaseMetaData dbMeta = conn.getMetaData();
                ResultSet tableRs = dbMeta.getTables(null, null, tableName, new String[]{"TABLE"});
                
                if (tableRs.next()) {
                    // 获取表注释
                    String tableComment = tableRs.getString("REMARKS");
                    if (tableComment == null || tableComment.isEmpty()) {
                        tableComment = tableName;
                    }
                    
                    // 表名转换为Java类名（驼峰命名）
                    String entityName = convertToCamelCase(tableName);
                    
                    // 获取表的列信息
                    ResultSet columnRs = dbMeta.getColumns(null, null, tableName, null);
                    List<Map<String, String>> fields = new ArrayList<>();
                    
                    System.out.println("表 " + tableName + " 的字段信息：");
                    
                    while (columnRs.next()) {
                        String columnName = columnRs.getString("COLUMN_NAME");
                        String columnComment = columnRs.getString("REMARKS");
                        String dataType = columnRs.getString("TYPE_NAME");
                        
                        System.out.println("字段名: " + columnName + ", 注释: " + columnComment + ", 类型: " + dataType);
                        
                        // 排除通用基础字段，这些字段不应由前端在创建或更新时提供
                        List<String> excludedColumns = Arrays.asList("id", "create_by", "create_time", "update_by", "update_time", "is_deleted");
                        if (!excludedColumns.contains(columnName.toLowerCase())) {
                            
                            Map<String, String> field = new HashMap<>();
                            field.put("name", convertToCamelCase(columnName));
                            field.put("comment", columnComment != null ? columnComment : columnName);
                            field.put("type", getJavaType(dataType));
                            fields.add(field);
                        }
                    }
                    
                    // 生成 CreateParam
                    generateFile(ve, "/templates/dto_create.java.vm", dtoReqPath + entityName + "CreateReqDTO.java", 
                                entityName, tableComment, fields, moduleName);
                    
                    // 生成 UpdateParam
                    generateFile(ve, "/templates/dto_update.java.vm", dtoReqPath + entityName + "UpdateReqDTO.java", 
                                entityName, tableComment, fields, moduleName);
                    
                    // 生成 VO
                    generateFile(ve, "/templates/vo.java.vm", voPath + entityName + "Vo.java", 
                                entityName, tableComment, fields, moduleName);
                }
                
                tableRs.close();
            }
            
            conn.close();
            System.out.println("自定义文件生成完成！");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 生成单个文件
     */
    private static void generateFile(VelocityEngine ve, String templatePath, String outputPath, 
                                    String entityName, String tableComment, List<Map<String, String>> fields,
                                    String moduleName) throws IOException {
        // 创建上下文
        VelocityContext context = new VelocityContext();
        context.put("entity", entityName);
        
        // 创建表对象，与模板中的变量名匹配
        Map<String, Object> tableMap = new HashMap<>();
        tableMap.put("comment", tableComment);
        
        // 创建字段列表
        List<Map<String, Object>> tableFields = new ArrayList<>();
        for (Map<String, String> field : fields) {
            Map<String, Object> tableField = new HashMap<>();
            tableField.put("keyFlag", false); // 非主键
            tableField.put("propertyName", field.get("name").substring(0, 1).toLowerCase() + field.get("name").substring(1)); // 首字母小写
            tableField.put("propertyType", field.get("type"));
            tableField.put("comment", field.get("comment"));
            tableFields.add(tableField);
        }
        tableMap.put("fields", tableFields);
        
        context.put("table", tableMap);
        context.put("author", author);
        context.put("date", LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
        context.put("package", Map.of("Parent", basePackageName));
        context.put("moduleName", moduleName);
        
        // 调试信息
        System.out.println("生成文件 " + outputPath + " 的上下文信息：");
        System.out.println("实体名: " + entityName);
        System.out.println("表注释: " + tableComment);
        System.out.println("字段数量: " + tableFields.size());
        for (Map<String, Object> field : tableFields) {
            System.out.println("字段: " + field.get("propertyName") + ", 注释: " + field.get("comment") + ", 类型: " + field.get("propertyType"));
        }
        
        // 获取模板
        Template template = ve.getTemplate(templatePath, "UTF-8");
        
        // 渲染模板
        StringWriter sw = new StringWriter();
        template.merge(context, sw);
        
        // 确保目录存在
        File file = new File(outputPath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        
        // 写入文件，指定UTF-8编码
        try (FileWriter writer = new FileWriter(file, java.nio.charset.StandardCharsets.UTF_8)) {
            writer.write(sw.toString());
        }
        
        System.out.println("生成文件：" + outputPath);
    }
    
    /**
     * 将下划线命名转换为驼峰命名
     */
    private static String convertToCamelCase(String name) {
        if (name == null || name.isEmpty()) {
            return "";
        }
        
        StringBuilder result = new StringBuilder();
        boolean nextUpper = false;
        
        for (int i = 0; i < name.length(); i++) {
            char c = name.charAt(i);
            
            if (c == '_') {
                nextUpper = true;
            } else {
                if (nextUpper) {
                    result.append(Character.toUpperCase(c));
                    nextUpper = false;
                } else {
                    // 首字母大写
                    if (i == 0) {
                        result.append(Character.toUpperCase(c));
                    } else {
                        result.append(c);
                    }
                }
            }
        }
        
        return result.toString();
    }
    
    /**
     * 将数据库类型转换为Java类型
     */
    private static String getJavaType(String dbType) {
        dbType = dbType.toUpperCase();
        
        if (dbType.contains("VARCHAR") || dbType.contains("CHAR") || dbType.contains("TEXT")) {
            return "String";
        } else if (dbType.contains("BIGINT")) {
            return "Long";
        } else if (dbType.contains("INT")) {
            return "Integer";
        } else if (dbType.contains("FLOAT") || dbType.contains("DOUBLE")) {
            return "Double";
        } else if (dbType.contains("DECIMAL")) {
            return "BigDecimal";
        } else if (dbType.contains("DATE") || dbType.contains("TIME")) {
            return "LocalDateTime";
        } else if (dbType.contains("BOOLEAN") || dbType.contains("BIT")) {
            return "Boolean";
        } else {
            return "String";
        }
    }

    /**
     * 将驼峰命名转换为kebab-case并转为复数
     */
    private static String toPluralKebabCase(String camelCase) {
        if (camelCase == null || camelCase.isEmpty()) {
            return "";
        }
        Pattern pattern = Pattern.compile("[A-Z]");
        Matcher matcher = pattern.matcher(camelCase);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "-" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        String kebabCase = sb.toString();
        if (kebabCase.startsWith("-")) {
            kebabCase = kebabCase.substring(1);
        }

        // 简单的复数规则
        if (kebabCase.endsWith("s") || kebabCase.endsWith("x") || kebabCase.endsWith("ch") || kebabCase.endsWith("sh")) {
            return kebabCase + "es";
        } else if (kebabCase.endsWith("y")) {
            return kebabCase.substring(0, kebabCase.length() - 1) + "ies";
        } else {
            return kebabCase + "s";
        }
    }

    public static void main(String[] args) {
        // 选择模块
        String moduleName = scanner("模块名 admin or portal");
        if (!"admin".equals(moduleName) && !"portal".equals(moduleName)) {
            throw new MybatisPlusException("模块名输入错误，目前只支持 admin 或 portal！");
        }

        // 项目根路径 (更健壮的获取方式)
        String projectPath;
        File currentDir = new File(System.getProperty("user.dir"));
        // 检查当前目录是否为项目根目录（包含pom.xml和关键模块）
        if (new File(currentDir, "pom.xml").exists() && new File(currentDir, "admin").exists() && new File(currentDir, "mbg").exists()) {
            projectPath = currentDir.getAbsolutePath();
        } else {
            // 否则，假定当前在模块目录（如mbg），父目录即为项目根目录
            projectPath = currentDir.getParent();
        }
        
        // Entity文件输出路径
        String entityPath = projectPath + "/mbg/src/main/java/com/yc/cloud/entity/";
        // Mapper文件输出路径
        String mapperPath = projectPath + "/mbg/src/main/java/com/yc/cloud/mapper/";
        // Mapper XML文件输出路径
        String mapperXmlPath = projectPath + "/mbg/src/main/resources/mapper/";
        // Service文件输出路径
        String servicePath = projectPath + "/" + moduleName + "/src/main/java/com/yc/cloud/" + moduleName + "/service/";
        // ServiceImpl文件输出路径
        String serviceImplPath = projectPath + "/" + moduleName + "/src/main/java/com/yc/cloud/" + moduleName + "/service/impl/";
        // Controller文件输出路径
        String controllerPath = projectPath + "/" + moduleName + "/src/main/java/com/yc/cloud/" + moduleName + "/controller/";

        // 使用 FastAutoGenerator 构建代码生成器
        FastAutoGenerator.create(URL, USERNAME, PASSWORD)
            // 全局配置
            .globalConfig(builder -> {
                builder.author(author)
                       .enableSwagger()
                       .dateType(DateType.TIME_PACK)
                       .disableOpenDir()
                       .outputDir(projectPath + "/mbg/src/main/java");
            })
            // 包配置
            .packageConfig(builder -> {
                Map<OutputFile, String> pathInfoMap = new HashMap<>();
                pathInfoMap.put(OutputFile.entity, entityPath);
                pathInfoMap.put(OutputFile.mapper, mapperPath);
                pathInfoMap.put(OutputFile.xml, mapperXmlPath);
                pathInfoMap.put(OutputFile.service, servicePath);
                pathInfoMap.put(OutputFile.serviceImpl, serviceImplPath);
                pathInfoMap.put(OutputFile.controller, controllerPath);
                
                builder.parent(basePackageName)
                       .entity("entity")
                       .mapper("mapper")
                       .service(moduleName + ".service")
                       .serviceImpl(moduleName + ".service.impl")
                       .controller(moduleName + ".controller")
                       .pathInfo(pathInfoMap);
            })
            // 策略配置
            .strategyConfig(builder -> {
                // 设置需要生成的表名
                String tables = scanner("表名，多个英文逗号分隔，生成所有表请输入[all]");
                List<String> tableList;
                if ("all".equals(tables)) {
                    // 生成所有表
                    tableList = Collections.emptyList();
                } else {
                    tableList = Arrays.asList(tables.split(","));
                    builder.addInclude(tableList);
                }
                
                // 设置过滤表前缀
                builder.addTablePrefix("t_", "c_");
                
                // Entity策略配置
                builder.entityBuilder()
                       .superClass(BaseEntity.class)
                       .enableLombok()
                       .enableTableFieldAnnotation()
                       .enableRemoveIsPrefix()
                       .enableColumnConstant()
                       .logicDeleteColumnName("deleted")
                       .versionColumnName("version")
                       .addTableFills(new Column("create_time", FieldFill.INSERT))
                       .addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE))
                       .enableFileOverride();
                
                // Controller策略配置
                builder.controllerBuilder()
                       .enableRestStyle()
                       .enableHyphenStyle();
                
                // Service策略配置
                builder.serviceBuilder()
                       .formatServiceFileName("%sService")
                       .formatServiceImplFileName("%sServiceImpl");
                
                // Mapper策略配置
                builder.mapperBuilder()
                       .enableMapperAnnotation()
                       .enableBaseResultMap()
                       .enableBaseColumnList()
                       .enableFileOverride();
                
                // 执行完毕后，使用自定义方法生成DTO和VO
                if (!tableList.isEmpty()) {
                    try {
                        generateCustomFiles(moduleName, tableList, projectPath);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            })
            // 注入自定义配置，供默认模板使用
            .injectionConfig(builder -> {
                Map<String, Object> customMap = new HashMap<>();
                customMap.put("swarmName", moduleName);
                // 自定义controllerMappingHyphen为复数形式
                builder.beforeOutputFile((tableInfo, objectMap) -> {
                    String entityName = tableInfo.getEntityName();
                    objectMap.put("controllerMappingHyphen", toPluralKebabCase(entityName));
                    // 注入CreateReqDTO和UpdateReqDTO包名
                    objectMap.put("CreateReqDTO", basePackageName + "." + moduleName + ".dto.request." + entityName + "CreateReqDTO");
                    objectMap.put("UpdateReqDTO", basePackageName + "." + moduleName + ".dto.request." + entityName + "UpdateReqDTO");
                    objectMap.put("Vo", basePackageName + "." + moduleName + ".vo." + entityName + "Vo");
                    objectMap.put("BasePageReqDTO", "com.yc.cloud.common.basic.dto.BasePageReqDTO");
                    objectMap.put("CommonPage", "com.yc.cloud.common.orm.CommonPage");
                    objectMap.put("ResponseResult", "com.yc.cloud.common.basic.api.ResponseResult");
                });
                builder.customMap(customMap);
            })
            // 模板配置
            .templateConfig(builder -> {
                builder.entity("/templates/entity.java.vm")
                       .service("/templates/service.java.vm")
                       .serviceImpl("/templates/serviceImpl.java.vm")
                       .mapper("/templates/mapper.java.vm")
                       .xml("/templates/mapper.xml.vm")
                       .controller("/templates/controller.java.vm");
            })
            // 使用Velocity模板引擎
            .templateEngine(new VelocityTemplateEngine())
            // 执行代码生成
            .execute();
        
        System.out.println("代码生成完成！");
    }
} 